/*
 * Compiler.java - Propeller LMM assembler
 *
 * Created on July 27, 2007, 12:30 PM
 *
 *  --------------------------------------------------------------------------------
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * --------------------------------------------------------------------------------
 *
 *
 * This compiler understands a LMM
 *
 * There are 2 kinds of assembler primitives, those native to the propeller
 * and the emulated ones. All emulated instructions are calls to a kernel
 * subrutine that performs the action
 * All addresses are absolute, symbols are of two kinds, absolute to the 512 long
 * memory space and absolute to the program
 *
 * Some starting symbols are needed, and they should be at the beginning
 * of the file:
 * krnl_pushi
 * krnl_pushr
 * krnl_pop
 *
 * Mar 09 2008 - Pacito.Sys
 *
 * To make this LMM Assembler more useful a set of changes are due:
 * - sections
 * - multiple symbols
 * - arithmetic expressions
 * 
 */

package org.pacito.plmmass;

/**
 * This class implements an assembler for the propeller
 * @author pacito
 */
public class Compiler {
    public final int MAX_COG_LONGS = 512;
    protected final int MAX_LITERAL = 0x1ff;
    protected final int MASK_INST = 0xfc000000;
    protected final int MASK_Z    = 0x02000000;
    protected final int MASK_C    = 0x01000000;
    protected final int MASK_R    = 0x00800000;
    protected final int MASK_I    = 0x00400000;
    protected final int MASK_COND = 0x003c0000;
    protected final int MASK_D    = 0x0003fe00;
    protected final int MASK_S    = 0x000001ff;
    
    protected final int IF_NEVER     =  0;
    protected final int IF_NC_AND_NZ =  1;
    protected final int IF_NC_AND_Z  =  2;
    protected final int IF_NC        =  3;
    protected final int IF_C_AND_NZ  =  4;
    protected final int IF_NZ        =  5;
    protected final int IF_C_NE_Z    =  6;
    protected final int IF_NC_OR_NZ  =  7;
    protected final int IF_C_AND_Z   =  8;
    protected final int IF_C_EQ_Z    =  9;
    protected final int IF_Z         = 10;
    protected final int IF_NC_OR_Z   = 11;
    protected final int IF_C         = 12;
    protected final int IF_C_OR_NZ   = 13;
    protected final int IF_C_OR_Z    = 14;
    protected final int IF_ALWAYS    = 15; // 1111  
    // read-only registers
    protected final int REG_FORB0 = 0x1f0;
    protected final int REG_FORB1 = 0x1f3;
    
    protected final int REG_ADDR  = 0x1f0;
    protected final int MASK_RAM  = 0x1ff;
    protected final int MASK_REGS = 0x00f;
    // Cog possible status
    
    protected String[] opcodes = { "wrbyte",  "rdbyte",  "wrword",  "rdword",  "wrlong",  "rdlong",   "clkset",  "cogid",  
                                   "coginit", "cogstop", "locknew", "lockret", "lockset", "lockclr",  "mul",     "muls",   
                                   "enc",     "ones",    "ror",     "rol",     "shr",     "shl",      "rcr",     "rcl",
                                   "sar",     "rev",     "mins",    "maxs",    "min",     "max",      "movs",    "movd",   
                                   "movi",    "jmp",     "call",    "ret",     "test",    "and",      "testn",   "andn",   
                                   "or",      "xor",     "muxc",    "muxnc",   "muxz",    "muxnz",    "add",     "cmp",    
                                   "sub",     "addabs",  "subabs",  "sumc",    "sumnc",   "sumz",     "sumnz",   "mov", 
                                   "neg",     "abs",     "absneg",  "negc",    "negnc",   "negz",     "negnz",   "cmps",
                                   "cmpsx",   "addx",    "cmpx",    "subx",    "adds",    "subs",     "addsx",   "subsx",
                                   "cmpsub",  "djnz",    "tjnz",    "tjz ",    "waitpeq", "waitpne ", "waitcnt", "waitvid", 
                                   "nop",     "=",       "long",    "word",    "byte",    "string",   "res",     "preamble" };
    
    protected final String SDIR_GLOBAL = ".global";
    protected final String SDIR_SECTION =  ".section";
    protected final String SDIR_ALIGN = ".align";
    
    protected final int DIR_GLOBAL = 1;
    protected final int DIR_SECTION = 2;
    protected final int DIR_ALIGN = 3;
    
    protected final int OP_MASK_OP = 0;
    protected final int OP_MASK_R = 1;
    protected final int OP_MASK_C = 2;
    protected final int OP_MASK_S = 3;
    
    //                                 opcode r  c  s
    // c = 0 des not accepts condition, no args (nop)
    // c = 1 accepts condition, 2 args
    // c = 2 accepts condition, no args (ret)
    // c = 3 i is forced to 1, just field d is used
    // c = 4 aceepts condition, 1 arg used (call)
    // c = 5 aceepts condition, 1 arg used (jmp)
    // c = 6 does not aceepts condition, 1 arg used (=)
    // c = 7 does not aceepts condition, 1 arg used (long)
    protected byte[][] opcodes_mask = { {  0, 0, 1, 0 }, // wrbyte
                                        {  0, 1, 1, 0 }, // rdbyte
                                        {  1, 0, 1, 0 }, // wrword
                                        {  1, 1, 1, 0 }, // rdword
                                        {  2, 0, 1, 0 }, // wrlong
                                        {  2, 1, 1, 0 }, // rdlong
                                        {  3, 0, 3, 0 }, // clkset
                                        {  3, 1, 3, 1 }, // cogid
                                        {  3, 0, 3, 2 }, // coginit
                                        {  3, 0, 3, 3 }, // cogstop
                                        {  3, 0, 3, 4 }, // locknew
                                        {  3, 0, 3, 5 }, // lockret
                                        {  3, 0, 3, 6 }, // lockset
                                        {  3, 0, 3, 7 }, // lockclr
                                        {  4, 1, 1, 0 }, // mul
                                        {  5, 1, 1, 0 }, // muls
                                        {  6, 1, 1, 0 }, // enc
                                        {  7, 1, 1, 0 }, // ones
                                        {  8, 1, 1, 0 }, // ror
                                        {  9, 1, 1, 0 }, // rol
                                        { 10, 1, 1, 0 }, // shr
                                        { 11, 1, 1, 0 }, // shl
                                        { 12, 1, 1, 0 }, // rcr
                                        { 13, 1, 1, 0 }, // rcl
                                        { 14, 1, 1, 0 }, // sar
                                        { 15, 1, 1, 0 }, // rev
                                        { 16, 1, 1, 0 }, // mins
                                        { 17, 1, 1, 0 }, // maxs
                                        { 18, 1, 1, 0 }, // min
                                        { 19, 1, 1, 0 }, // max
                                        { 20, 1, 1, 0 }, // movs
                                        { 21, 1, 1, 0 }, // movd
                                        { 22, 1, 1, 0 }, // movi
                                        { 23, 0, 5, 0 }, // jmp
                                        { 23, 1, 4, 0 }, // call
                                        { 23, 0, 2, 0 }, // ret
                                        { 24, 0, 1, 0 }, // test
                                        { 24, 1, 1, 0 }, // and
                                        { 25, 0, 1, 0 }, // testn
                                        { 25, 1, 1, 0 }, // andn
                                        { 26, 1, 1, 0 }, // or
                                        { 27, 1, 1, 0 }, // xor
                                        { 28, 1, 1, 0 }, // muxc
                                        { 29, 1, 1, 0 }, // muxnc
                                        { 30, 1, 1, 0 }, // muxz
                                        { 31, 1, 1, 0 }, // muxnz
                                        { 32, 1, 1, 0 }, // add
                                        { 33, 0, 1, 0 }, // cmp
                                        { 33, 1, 1, 0 }, // sub
                                        { 34, 1, 1, 0 }, // addabs
                                        { 35, 1, 1, 0 }, // subabs
                                        { 36, 1, 1, 0 }, // sumc
                                        { 37, 1, 1, 0 }, // sumnc
                                        { 38, 1, 1, 0 }, // sumz
                                        { 39, 1, 1, 0 }, // sumnz
                                        { 40, 1, 1, 0 }, // mov
                                        { 41, 1, 1, 0 }, // neg
                                        { 42, 1, 1, 0 }, // abs
                                        { 43, 1, 1, 0 }, // absneg
                                        { 44, 1, 1, 0 }, // negc
                                        { 45, 1, 1, 0 }, // negnc
                                        { 46, 1, 1, 0 }, // negz
                                        { 47, 1, 1, 0 }, // negnz
                                        { 48, 0, 1, 0 }, // cmps
                                        { 49, 0, 1, 0 }, // cmpsx
                                        { 50, 1, 1, 0 }, // addx
                                        { 51, 0, 1, 0 }, // cmpx
                                        { 51, 1, 1, 0 }, // subx
                                        { 52, 1, 1, 0 }, // adds
                                        { 53, 1, 1, 0 }, // subs
                                        { 54, 1, 1, 0 }, // addsx
                                        { 55, 1, 1, 0 }, // subsx
                                        { 56, 1, 1, 0 }, // cmpsub
                                        { 57, 1, 1, 0 }, // djnz
                                        { 58, 0, 1, 0 }, // tjnz
                                        { 59, 0, 1, 0 }, // tjz
                                        { 60, 0, 1, 0 }, // waitpeq
                                        { 61, 0, 1, 0 }, // waitpne
                                        { 62, 1, 1, 0 }, // waitcnt
                                        { 63, 0, 1, 0 }, // waitvid
                                        {  0, 0, 0, 0 }, // nop
                                        {  0, 0, 6, 0 }, // =
                                        {  0, 0, 7, 0 }, // long
                                        {  0, 0, 8, 0 }, // word
                                        {  0, 0, 9, 0 }, // byte
                                        {  0, 0,10, 0 }, // string
                                        {  0, 0,11, 0 }, // res
                                        {  0, 0,12, 0 }  // preamble
                                        
                                    };
    
    protected String[] regs = { "PAR", "CNT", "INA", "INB", "OUTA", "OUTB", "DIRA", "DIRB", 
                                "CTRA", "CTRB", "FRQA", "FRQB", "PHSA", "PHSB", "VCFG", "VSCL" };
    
    protected String[] conds = { "if_always",   "if_nc_and_nz", "if_nc_and_z", "if_nc", 
                                 "if_c_and_nz", "if_nz",        "if_c_ne_z",   "if_nc_or_nz", 
                                 "if_c_and_z",  "if_c_eq_z",    "if_z",        "if_nc_or_z", 
                                 "if_c",        "if_c_or_nz",   "if_c_and_z",  "if_never",
                                 "if_e",        "if_ne",        "if_a",        "if_b",
                                 "if_ae",       "if_be",        "if_z_eq_c",   "if_z_ne_c",
                                 "if_z_and_c",  "if_z_and_nc",  "if_nz_and_c", "if_nz_and_nc",
                                 "if_z_or_c",   "if_z_or_nc",   "if_nz_or_c",  "if_nz_or_nc"
                                };
    
    protected int[] condmap = {  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
                                10, 5, 1,12, 3,14, 9, 6, 8, 2,  4,  1, 14, 11, 13,  7 };
                                
    protected java.util.Vector <Symbol> symboltable;
    protected long[] compiledCode;
    protected int compiledCodeLength, errorLine;
    //protected String source;
    //protected String[] tokenizedline;
    protected int lastError;
    protected String lastErrorStr = "";
    protected String notFoundSymbol = "";
    protected String currSection, currSectName;
    protected int sectAddr;
    
    protected final String[] ERR_STR = { "Compilation sucessful", "Duplicated symbol",
                                        "Condition/effect not allowed here",
                                        "Instruction Unknown",
                                        "Symbol not found",
                                        "Symbol only line",
                                        "Duplicated wc or wz field",
                                        "Argument(s) missing",
                                        "Destination is read-only",
                                        "Garbage at end of line",
                                        "COG section exceeds 496 longs",
                                        "Duplicated section",
                                        "Too few arguments for directive",
                                        "Include file not found"};
    
    
    protected final int ERR_NONE      = 0; // no error
    protected final int ERR_SYMEXISTS = -1; // symbol exists
    protected final int ERR_INVCE   = -2; // misplaced condition/effect
    protected final int ERR_UKNINST   = -3; // unknown instruction
    protected final int ERR_SYMNF     = -4; // symbol not found
    protected final int ERR_EMPTY     = -5; // empty line
    protected final int ERR_DUPWX     = -6; // duplicated WC or WZ field
    protected final int ERR_ARGMISS = -7; // Argument(s) missing
    protected final int ERR_RDONLY = -8; // memory position is read-only (INA/INB)
    protected final int ERR_GARBAGE = -9; // garbage at end of line
    protected final int ERR_SECTTOOLONG = -10; // section too long
    protected final int ERR_DUPSECT = -11; //Duplicated section
    protected final int ERR_DIRNOARGS = -12; // Too few arguments for directive
    protected final int ERR_NOINC = -13; // include file not found
    
    // position of fields in the tokenized line 
    
    protected final int IDX_SYM  = 0;
    protected final int IDX_COND = 1;
    protected final int IDX_INST = 2;
    protected final int IDX_ARG1 = 3;
    protected final int IDX_ARG2 = 4;
    protected final int IDX_WC   = 5;
    protected final int IDX_WZ   = 6;
    
    protected final String WC = "wc";
    protected final String WZ = "wz";
    
    protected int stackPtr;
    protected long[] stack = new long[32];
    
    protected TokenizeCode tokenizer = new TokenizeCode();
    // tokenized code
    protected String t_sym;
    protected String t_cond;
    protected String t_inst;
    protected java.util.Vector<String> t_args;
    protected java.util.Vector<String> t_effs;
    
    // List of included files
    protected java.util.Vector<String> includeFiles;
    /** Creates a new instance of compiler */
    
    public Compiler(String inputFileName, String outputBinFileName, String outputListFileName) {
        symboltable = new java.util.Vector<Symbol>();
        String line;
        String mainSection;
        
        java.util.Vector <String> code = new java.util.Vector<String>();
        java.util.Vector <String> includeFiles = new java.util.Vector<String>();
        // first included files, rest in handled in compilePass1
        includeFiles.add(inputFileName);
        int addr;
        
        // Adds registers as symbols
        for (int i = 0; i < 16; i++)
            symboltable.add(new Symbol(regs[i], REG_ADDR + i, "COG"));
        
        java.io.BufferedReader inputFile;
        java.io.FileOutputStream outputBin;
        java.io.BufferedWriter outputList;
    
        try {
            inputFile = new java.io.BufferedReader( new java.io.FileReader(inputFileName));
            outputBin = new java.io.FileOutputStream(outputBinFileName);
            outputList = new java.io.BufferedWriter(new java.io.FileWriter(outputListFileName));
        } catch (java.io.IOException e) {
            System.out.println("Input/Output Error");
            return;
        }
        try {
        outputList.write("; pacito LMM Assembler (c) 2007-2008 Pacito.Sys\n\n");
        outputList.write("; input file : " + inputFileName + ", output file : " + outputBinFileName + "\n");
        outputList.write("; this file : " + outputListFileName + "\n\n");
        
        // Compile main loop
        int linenum = 0;
        mainSection = "HUB";
        currSection = "HUB";
        sectAddr = 0;
        addr = 0;
        line = inputFile.readLine();
        while (line != null) {
            code.add(line);
            addr += compileLinePass1(code, line, addr, linenum++, outputList);
            line = inputFile.readLine();
        }
        inputFile.close();
        
        // second pass
        int l = code.size();
        
        mainSection = "HUB";
        currSection = "HUB";
        sectAddr = 0;
        addr = 0;
        for (int i = 0; i < l; i++) {
            addr += compileLinePass2(code.get(i), addr, i, outputBin, outputList);
        }
        
        // Symbol table output
        outputList.write("; Symbol table\n\n");
        
        for (int i = 0; i < symboltable.size(); i++) {
            outputList.write(symboltable.get(i).getHexAddr() + " = " + symboltable.get(i).getSymbolName() + "\n");
        }
        
        
        outputBin.close();
        outputList.close();
        }catch (java.io.IOException e) {
            System.out.println("Input/Output Error");
            return;
        }
        
    }
    
    private int compileLinePass1(java.util.Vector <String> code, String line, int addr, int linenum, java.io.BufferedWriter list){
        int codeLength = 0;
        Instruction i;
        errorLine = -1;
        
        // first pass, symbol creation, "=" silently ignored !!"
        
        if (line.length() < 1) {
            try {
                list.write(line + "\n");
            } catch (java.io.IOException e) {}
            return codeLength;
        }
        lastError = tokenizer.tokenize(line);
        t_sym = tokenizer.getSymbol();
        t_cond = tokenizer.getCondition();
        t_inst = tokenizer.getInstruction();
        t_args = tokenizer.getArgs();
        t_effs = tokenizer.getEffects();
        if (lastError != ERR_NONE) {
            reportError(linenum); // Line number with error
            try {
                list.write(lastErrorStr + "\n");
            } catch (java.io.IOException e) {}
            return 0; // Line number with error
        }
        lastError = prepareTokenized(false);
        if (lastError != ERR_NONE) {
            reportError(linenum); // Line number with error
            try {
                list.write(lastErrorStr + "\n");
            } catch (java.io.IOException e) {}
            return 0; // Line number with error
        }
        // we handle included files here
        if (t_inst != null && t_inst.equalsIgnoreCase(".include") && t_args.size() == 1) {
            java.io.BufferedReader inputFile;
            try {
                inputFile = new java.io.BufferedReader( new java.io.FileReader(t_args.get(0)));
                // Compile main loop
                int newlinenum = 0;
                String newline = inputFile.readLine();
                while (newline != null) {
                code.add(newline);
                addr += compileLinePass1(code, newline, addr, newlinenum++, list);
                newline = inputFile.readLine();
            }
            inputFile.close();
            } catch (java.io.IOException e) {
                lastError = ERR_NOINC;
                return 0;
            }  
        }
        
        i = compileTokenized(addr, true); // we allow it to create symbols from labels and equates
        if (i != null) {
            addr += i.getSize();
            return i.getSize();
        }
        return 0;
    }
    
    private int compileLinePass2(String line, int addr, int linenum, java.io.FileOutputStream bin, java.io.BufferedWriter list){
        int codeLength = 0;
        int j, l, l1;
        boolean h;
        byte[] d;
        Instruction i;
        errorLine = -1;
   
        lastError = tokenizer.tokenize(line);
        if (lastError != ERR_NONE) {
            reportError(linenum); // Line number with error
            try {
                list.write(lastErrorStr + "\n");
            } catch (java.io.IOException e) {}
            return 0; // Line number with error
        }
        t_sym = tokenizer.getSymbol();
        t_cond = tokenizer.getCondition();
        t_inst = tokenizer.getInstruction();
        t_args = tokenizer.getArgs();
        t_effs = tokenizer.getEffects();
        
        lastError = prepareTokenized(true);
        
        if (lastError != ERR_NONE) {
            reportError(linenum); // Line number with error
            try {
                list.write(lastErrorStr + "\n");
            } catch (java.io.IOException e) {}
            return 0; // Line number with error
        }
        i = compileTokenized(addr, false);
        if (i != null) // if the lined contained a 
            try {
                d = i.getData();
                l = i.getSize();
                for (j = 0; j < l; j++)
                    bin.write((int) d[j]);
                
                if (l >= 4) {
                    l1 = l & 0xfffc;
                    for (j = 0; j < l1; j+=4) {
                        if (j == 0)
                            list.write(String.format("%08x  %02x%02x%02x%02x  %s\n", addr, d[0], d[1], d[2], d[3], line));
                        else
                            list.write(String.format("          %02x%02x%02x%02x\n", d[j], d[j+1], d[j+2], d[j+3]));
                    }
                    l = l - l1;
                    if (l == 1)
                        list.write(String.format("          %02x\n", d[j]));
                    if (l == 2)
                        list.write(String.format("          %02x%02x\n", d[j], d[j+1]));
                    if (l == 3)
                        list.write(String.format("          %02x%02x%02x\n", d[j], d[j+1], d[j+2]));
                }
                else {
                    if (l == 1)
                        list.write(String.format("%08x  %02x        %s\n", addr, d[0], line));
                    if (l == 2)
                        list.write(String.format("%08x  %02x%02x      %s\n", addr, d[0], d[1], line));
                    if (l == 3)
                        list.write(String.format("%08x  %02x%02x%02x    %s\n", addr, d[0], d[1], d[2], line));
                }
                
            } catch (java.io.IOException e) {}
                //compiledCode[addr++] = i.getOpcode();
        else // maybe an error
        {
            if (lastError != ERR_NONE) {
                reportError(linenum); // Line number with error
                try {
                    list.write(lastErrorStr + "\n");
                } catch (java.io.IOException e) {}
                return 0; // Line number with error
            }
            else {
                try {
                    list.write(String.format("                    %s\n", line));
                } catch (java.io.IOException e) {}
                return 0;
            }
        }
        
        compiledCodeLength = addr;
        lastError = ERR_NONE;
        return i.getSize();
    }
    
    /**
     * Returns the compiled code as a long[]
     *
     */
    public long[] getCompiledCode() {
        return compiledCode;
    }
    
    /**
     * Returns the number of symbols created
     *
     */
    public int getSymbolCount() {
        return symboltable.size();
    }
    
    /**
     * Gets the amount of code generated (in longs)
     */
    public int getCodeSize() {
        return compiledCodeLength;
    }
    
    /**
     * Returns the line were an error occurred
     *
     */
    public int getErrorLine() {
        return errorLine;
    }
    
    /**
     * Returns the last error as a String
     *
     */
    public String getErrorString() {
        return lastErrorStr;
    }
    
    /**
     * loads a symbol table
     *
     */
    
    public void setSymbolTable(java.util.Vector <Symbol> st) {
        symboltable = st;
    }
    
    /**
     * returns the symbol table
     *
     */
    
    public java.util.Vector <Symbol> getSymbolTable() {
        return symboltable;
    }
    /**
     * Generates the error string and returns the lastError
     * 
     */
    
    protected int reportError(int linenum) {
        lastErrorStr = "Line " + linenum + ": " + ERR_STR[Math.abs(lastError)];
        switch (lastError) {
            case ERR_NONE: 
                lastErrorStr += ", " + getCodeSize() + " long(s) used, " + getSymbolCount() + " symbol(s) created";
                return lastError;
            case ERR_UKNINST:
                lastErrorStr += "(" + notFoundSymbol + ")";
                break;
            case ERR_SYMNF:
                lastErrorStr += "(" + notFoundSymbol + ")";
                break;
            case ERR_SECTTOOLONG:
                lastErrorStr += "(" +  currSection + ")";
                break;
            case ERR_DIRNOARGS:
                lastErrorStr += "(" + notFoundSymbol + ")";
                break;
        }
        return lastError;
    }
    /**
     * Converts a line of code into a tokenized form
     *
     * Symbol condition instruction arg1[,arg2]
     *
     * Tokenized form (Strings):
     * tokenizedline[0] = Symbol Name if any
     * tokenizedline[1] = condition
     * tokenizedline[2] = instruction name
     * tokenizedline[3] = first argument
     * tokenizedline[4] = second argument
     *
     * Does test if condition is valid
     */
    
    protected int prepareTokenized(boolean ignoreSyms) {
        String s;
        int err = ERR_NONE;
        int stage = 0;
        
        if (t_sym == null && t_inst == null)
            return ERR_NONE; // empty line, no error, tokenized line contains nulls
        
        /* we check that :
         * - directives have argument(s), and no condition or effects
         *
         * - invalid instructions do not have effects or conditions (long, byte, ..
         */
        
        if (isDirective(t_inst)) {
            notFoundSymbol = t_inst;
            switch (getDirective(t_inst))
            {
                case DIR_GLOBAL: // one more arg
                    if (t_args.size() == 1) {
                        if (symbolExists(t_args.get(0)) && !ignoreSyms)
                            return ERR_SYMEXISTS;
                        return ERR_NONE;
                    }
                    break;
                    
                case DIR_ALIGN:
                    if (t_args.size() == 1)
                        return ERR_NONE;
                    break;
                case DIR_SECTION:
                    if (t_args.size() == 2) {
                        return ERR_NONE;
                    }
                    break;
            }
            return ERR_DIRNOARGS;
        }
        // check for duplicated symbol
        if (symbolExists(t_sym) && !ignoreSyms)
            return ERR_SYMEXISTS;
        
        if (isConstant(t_inst)) {
            if (isCondition(t_cond) && isEffect(t_effs))
                return ERR_INVCE;
        }
        
        return err;
    }
    /** Checks if the argument is a valid directive
     *
     */
    protected boolean isDirective(String s) {
        if (s == null)
            return false;
        if (s.equalsIgnoreCase(SDIR_ALIGN))
            return true;
        if (s.equalsIgnoreCase(SDIR_SECTION))
            return true;
        if (s.equalsIgnoreCase(SDIR_GLOBAL))
            return true;
        return false;
    }
    
    /** Checks if the argument is a valid directive
     *
     */
    protected int getDirective(String s) {
        if (s.equalsIgnoreCase(SDIR_ALIGN))
            return DIR_ALIGN;
        if (s.equalsIgnoreCase(SDIR_SECTION))
            return DIR_SECTION;
        if (s.equalsIgnoreCase(SDIR_GLOBAL))
            return DIR_GLOBAL;
        return 0;
    }
    
    /**
     * Compiles an instruction
     * 
     * Returns an <code>Instruction</code>, if and only if the tokenized line contains a valid instruction
     * A symbol can be created if it allowed and if one exists
     *
     * @param addr int Address of the current point
     * @param createSymbol boolean flags if a symbol can be created if one exists
     */
    
    protected Instruction compileTokenized(int addr, boolean createSymbol) {
        Instruction i = new Instruction();
        byte[] op_mask;
        int cond = IF_ALWAYS;
        long op;
        boolean dataSet = false;
        Symbol sym = null;
        
        if (t_cond != null)
            cond = getConditionMask(t_cond);
        
        if (t_inst == null) {// empty line, no instruction, we check if a symbol can be created
            if (t_sym != null && createSymbol) {
               sym = new Symbol(t_sym, addr - sectAddr, currSection);
               symboltable.add(sym); // adds symbol to symbol table
            }
            return null;
        }
        
        lastError = ERR_NONE;
        // we process directives
        if (isDirective(t_inst)) {
            switch (getDirective(t_inst))
            {
                case DIR_GLOBAL: // one more arg
                    // creates a global symbol
                    if (createSymbol && !symbolExists(t_args.get(0))) {
                        sym = new Symbol(t_args.get(0), addr, "HUB"); // global symbol !
                        sym.setType(sym.SYM_HUB);
                        symboltable.add(sym); // adds symbol to symbol table
                    }
                    return null;
                case DIR_ALIGN:
                    long tmp = getSymbolAddr(t_args.get(0));
                    if (lastError != ERR_NONE)
                        return null; // symbol not found
                    if (tmp < 0)
                        return null; // ignore
                    if (tmp > 15)
                        tmp = 15;
                    tmp = (addr + (1 << tmp) - 1) & ((0xffffff >> tmp) << tmp);
                    if (tmp > addr) {
                        i = new Instruction();
                        i.setData(new byte[(int) (tmp - addr)]);
                        i.setAddr(addr);
                        if (t_sym != null && createSymbol) {
                            sym = new Symbol(t_sym, addr - sectAddr, currSection);
                            symboltable.add(sym); // adds symbol to symbol table
                        }
                        i.setSymbol(sym);
                        return i;
                    }  
                    return null;
                    
                case DIR_SECTION:
                    /* section change
                     * A section type is provided and a name
                     * names should not be repeated
                     * local addresses start at a sections' start
                     */
                    if (t_args.get(0).equalsIgnoreCase("COG")) {
                        // new COG section
                        sectAddr = addr;
                        currSectName = t_args.get(1);
                        currSection = t_args.get(0);
                        return null;
                    }
                    
                    if (t_args.get(0).equalsIgnoreCase("HUB")) {
                        sectAddr = 0;
                        currSection = t_args.get(1);
                        currSectName = t_args.get(0);
                        
                        return null; 
                    }
                    
                    if (currSection.equalsIgnoreCase("LMM")) {
                        sectAddr = addr;
                        currSection = t_args.get(0);
                        currSectName = t_args.get(2);
                        return null;
                         
                    }
                    
                    break;
                    
            }
        }
        
        op_mask = getOpcodeMask(t_inst);
        if (op_mask == null) {
            return null;
        }
        op = (op_mask[OP_MASK_OP] << 26);
        if ((t_args.size() == 0) && (op_mask[OP_MASK_C] != 0)
            && (op_mask[OP_MASK_C] != 2)){
            lastError = ERR_ARGMISS; // too few args
            return null;
        }
        switch (op_mask[OP_MASK_C]) {
            case 0: // only used by nop, no condition accepted
                break;
            case 1: // condition is accepted
                op |= cond << 18;
                op |= ((op_mask[OP_MASK_R] == 1) | isEffectWR(t_effs)) ? MASK_R:0;
                op |= isEffectWC(t_effs) ? MASK_C:0;
                op |= isEffectWZ(t_effs) ? MASK_Z:0;
                if ((getSymbolAddr(t_args.get(0)) >= REG_FORB0) &&
                    (getSymbolAddr(t_args.get(0)) <= REG_FORB1)) {
                    lastError = ERR_RDONLY; // read-only destination
                    return null;
                }
                op |= getSymbolAddr(t_args.get(0)) << 9; // d
                if (t_args.get(1) != null) {
                    if (t_args.get(1).charAt(0) == '#')
                        op |= getLiteral(t_args.get(1)) | MASK_I;
                    else op |= getSymbolAddr(t_args.get(1));
                } else {
                    lastError = ERR_ARGMISS; // too few args
                    return null;
                }
                break;
            case 2: // used by ret (is a jmp, written at compile time)
                op |= cond << 18;
                op |= ((op_mask[OP_MASK_R] == 1) | isEffectWR(t_effs)) ? MASK_R:0;
                op |= isEffectWC(t_effs) ? MASK_C:0;
                op |= isEffectWZ(t_effs) ? MASK_Z:0;
                op |= MASK_I;
                break;
            case 3: // field s contains part of the opcode mask, only filled d is used
                op |= cond << 18;
                op |= ((op_mask[OP_MASK_R] == 1) | isEffectWR(t_effs)) ? MASK_R:0;
                op |= isEffectWC(t_effs) ? MASK_C:0;
                op |= isEffectWZ(t_effs) ? MASK_Z:0;
                op |= getSymbolAddr(t_args.get(0)) << 9; // d
                op |= op_mask[OP_MASK_S];
                break;
            case 4: // call d, s  d is destination for jmp, s is jmp
                op |= cond << 18;
                op |= ((op_mask[OP_MASK_R] == 1) | isEffectWR(t_effs)) ? MASK_R:0;
                op |= isEffectWC(t_effs) ? MASK_C:0;
                op |= isEffectWZ(t_effs) ? MASK_Z:0;
                if (t_args.get(0).charAt(0) != '#') {
                    lastError = ERR_ARGMISS; // too few args
                    return null;
                }
                op |= getLiteral(t_args.get(0)) | MASK_I;
                op |= getSymbolAddr(t_args.get(0).substring(1) + "_ret") << 9; // d
                
                break;
            case 5: // jmp
                op |= cond << 18;
                op |= ((op_mask[OP_MASK_R] == 1) | isEffectWR(t_effs)) ? MASK_R:0;
                op |= isEffectWC(t_effs) ? MASK_C:0;
                op |= isEffectWZ(t_effs) ? MASK_Z:0;
                if (t_args.get(0).charAt(0) == '#')
                    op |= getLiteral(t_args.get(0)) | MASK_I;
                else op |= getSymbolAddr(t_args.get(0)); // s
                break;
            case 6: // equate, define, = just a symbol, no real instruction
                if (!createSymbol)
                    return null; // ignores equates in second pass
                if ((t_sym != null) && (t_args.get(0) != null)) {
                    sym = new Symbol(t_sym, (int) getSymbolAddr(t_args.get(0)), "DEF");
                    symboltable.add(sym);
                    return null; // no instruction
                }
                break;
            case 7: // reserve space, long
                for (int j = 0; j < t_args.size(); j++)
                    i.addLong(getFullSymbolAddr(t_args.get(j)));
                dataSet = true;
                break;
            case 8: // word
                for (int j = 0; j < t_args.size(); j++)
                    i.addWord((int)getFullSymbolAddr(t_args.get(j)));
                dataSet = true;
                break;
            case 9: // byte
                for (int j = 0; j < t_args.size(); j++)
                    i.addByte((int)getFullSymbolAddr(t_args.get(j)));
                dataSet = true;
                break;
            case 10: // string
                for (int j = 0; j < t_args.size(); j++) {
                    if (t_args.get(j).charAt(0) == '\"' || t_args.get(j).charAt(0) == '\'')
                        i.addString(t_args.get(j));
                    else // A string can contain embidded bytes
                        i.addByte((int)getFullSymbolAddr(t_args.get(j)));
                }
                dataSet = true;
                break;
            case 11: // res
                
                break;
            case 12: // preamble
                
                break;
        }
        // now we can create the symbol, if it does not exists and is not an equate
        
        if (t_sym != null && createSymbol) {
            sym = new Symbol(t_sym, addr - sectAddr, currSection);
            symboltable.add(sym); // adds symbol to symbol table
        }
        if (lastError != ERR_NONE && !createSymbol) { // symbol not found during second pass
            return null;
        }
        if (!dataSet) {  
            i.addLong(op);
        }
        i.setAddr(addr);
        i.setSymbol(sym);
        
        
        return i;
    }    
    
    protected boolean symbolExists(String s) {
        Symbol sym;
        if (s == null)
            return false;
        int l = symboltable.size();
        for (int i = 0; i < l; i++) {
            sym = (Symbol) symboltable.elementAt(i);
            if (sym.match(s))
                return true;
        }
        return false;
    }
    
    protected boolean isCondition(String s) {
        int l = conds.length;
        if (s == null)
            return false;
        
        for (int i = 0; i < l; i++)
            if (s.equalsIgnoreCase(conds[i]))
                return true;
        return false;
    }
    
    protected boolean isInstruction(String s) {
        int l = opcodes.length;
        if (s == null)
            return false;
        
        for (int i = 0; i < l; i++)
            if (s.equalsIgnoreCase(opcodes[i]))
                return true;
        return false;
    }
    
    protected boolean isEffect(java.util.Vector<String> s) {
        int l = s.size(), wc = 0, wz = 0, wr = 0;
        if (l == 0)
            return false;
        String str;
        for (int i = 0; i < l; i++) {
            str = s.get(i);
            if (str.equalsIgnoreCase("wc")) wc++;
            else
            if (str.equalsIgnoreCase("wz")) wz++;
            else
            if (str.equalsIgnoreCase("wr")) wr++;
            else
                return false;
        }
        if (wc == 1 || wz == 1 || wr == 1)
            return true;
        return false;
    }
    
    protected boolean isEffectWC(java.util.Vector<String> s) {
        int l = s.size(), wc = 0, wz = 0, wr = 0;
        if (l == 0)
            return false;
        String str;
        for (int i = 0; i < l; i++) {
            str = s.get(i);
            if (str.equalsIgnoreCase("wc")) return true;
        }
        return false;
    }
    
    protected boolean isEffectWZ(java.util.Vector<String> s) {
        int l = s.size(), wc = 0, wz = 0, wr = 0;
        if (l == 0)
            return false;
        String str;
        for (int i = 0; i < l; i++) {
            str = s.get(i);
            if (str.equalsIgnoreCase("wz")) return true;
        }
        return false;
    }
    
    protected boolean isEffectWR(java.util.Vector<String> s) {
        int l = s.size(), wc = 0, wz = 0, wr = 0;
        if (l == 0)
            return false;
        String str;
        for (int i = 0; i < l; i++) {
            str = s.get(i);
            if (str.equalsIgnoreCase("wr")) return true;
        }
        return false;
    }
    /** Returns true is the argument is a constant holder, long, word, byte, string, res
     *
     */
    protected boolean isConstant(String cnt) {
        if (cnt == null)
            return false;
        if (cnt.equalsIgnoreCase("long"))
            return true;
        if (cnt.equalsIgnoreCase("word"))
            return true;
        if (cnt.equalsIgnoreCase("byte"))
            return true;
        if (cnt.equalsIgnoreCase("string"))
            return true;
        if (cnt.equalsIgnoreCase("res"))
            return true;
        return false;
    }
    
    protected byte[] getOpcodeMask(String s) {
        int l = opcodes.length;
        if (s == null)
            return null;
        
        for (int i = 0; i < l; i++)
            if (s.equalsIgnoreCase(opcodes[i]))
                return opcodes_mask[i];
        return null;
    }
    
    protected int getConditionMask(String s) {
        int l = conds.length;
        if (s == null)
            return IF_ALWAYS;
        
        for (int i = 0; i < l; i++)
            if (s.equalsIgnoreCase(conds[i]))
                return condmap[i]; // gets the condition from the mapping Name -> cond
        return IF_ALWAYS;
    }

    /**
     * Returns the address of the current symbol if it is found or
     * the number if is a valid number, zero otherwise
     *
     */
    protected long getSymbolAddr(String s) {
        Symbol sym;
        if (s == null)
            return 0;
        // check first if it is not a number
        try {
            long i = parseNumber(s);
            return i & MAX_LITERAL;
        } catch (java.lang.NumberFormatException e) {
            int l = symboltable.size();
            for (int i = 0; i < l; i++) {
                sym = (Symbol) symboltable.elementAt(i);
                if (sym.match(s))
                    return sym.getAddr();
            }
        }
        notFoundSymbol = s;
        lastError = ERR_SYMNF; // symbol not found
        return 0;
    }
    /**
     * Returns the address of the current symbol if it is found or
     * the number if it is a valid number, zero otherwise
     *
     */
    protected long getFullSymbolAddr(String s) {
        Symbol sym;
        if (s == null)
            return 0;
        // check first if it is not a number
        try {
            long i = parseNumber(s);
            return i;
        } catch (java.lang.NumberFormatException e) {
            int l = symboltable.size();
            for (int i = 0; i < l; i++) {
                sym = (Symbol) symboltable.elementAt(i);
                if (sym.match(s))
                    return sym.getAddr();
            }
        }
        notFoundSymbol = s;
        lastError = ERR_SYMNF; // symbol not found
        return 0;
    }
    
    protected long getLiteral(String s) {
        if (s == null)
            return 0;
        
        String ns = s.substring(1);
        long i = 0;
        
        try {
            i =  parseNumber(ns);
        } catch (java.lang.NumberFormatException e) {
            i = getSymbolAddr(ns);
            if (i == 0)
                return -1;
        }
        return i & MAX_LITERAL;
    }
    
    /**
     * Parses a string as a number
     *
     * Accepts:
     *
     * Hex ($), Decimal () and binary (%) numbers
     * Separated by '_' if present
     */
    
    protected long parseNumber(String n) throws java.lang.NumberFormatException {
        long i = 0;
        int j, p = 0;
        n = n.toLowerCase();
        switch (n.charAt(0)) {
            case '$': // Hex
                p = 1;
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (n.charAt(p)) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9': 
                        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                            i = (i << 4) | (j > 0x49 ? j-87:j-48);
                            break;
                        case '_':
                            break;
                        default:
                            throw new java.lang.NumberFormatException();
                    }
                    p++;
                }
                break;
            case '%': // BIN
                p = 1;
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (n.charAt(p)) {
                        case '0': case '1': 
                            i = (i << 1) | (j-48);
                            break;
                        case '_':
                            break;
                        default:
                            throw new java.lang.NumberFormatException();
                    }
                    p++;
                }
                break;
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9': case '_':
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (j) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9': 
                            i = (i * 10) + (j-48);
                            break;
                        case '_':
                            break;
                        default:
                            throw new java.lang.NumberFormatException();
                    }
                    p++;
                }
                break;
            default:
                throw new java.lang.NumberFormatException();
        }
        return i;
    }
    
    /**
     * Parses a string as a number to find its boundaries
     *
     * Accepts:
     *
     * Hex ($), Decimal () and binary (%) numbers
     * Separated by '_' if present
     */
    
    protected int findNumber(String n) {
        long i = 0;
        int j, p = 0;
        n = n.toLowerCase();
        switch (n.charAt(0)) {
            case '$': // Hex
                p = 1;
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (n.charAt(p)) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9': 
                        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
                            i = (i << 4) | (j > 0x49 ? j-87:j-48);
                            break;
                        case '_':
                            break;
                        default:
                            if (p > 1) return p; // length of valid number
                    }
                    p++;
                }
                break;
            case '%': // BIN
                p = 1;
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (n.charAt(p)) {
                        case '0': case '1': 
                            i = (i << 1) | (j-48);
                            break;
                        case '_':
                            break;
                        default:
                            if (p > 1) return p; // length of valid number
                    }
                    p++;
                }
                break;
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9': case '_':
                while (p < n.length()) {
                    j = (int) n.charAt(p);
                    switch (j) {
                        case '0': case '1': case '2': case '3': case '4':
                        case '5': case '6': case '7': case '8': case '9': 
                            i = (i * 10) + (j-48);
                            break;
                        case '_':
                            break;
                        default:
                            if (p > 0) return p; // length of valid number
                    }
                    p++;
                }
                break;
            default:
                return 0; // No valid number
        }
        if (p == 0) {
            if (n.charAt(0) != '$' && n.charAt(0) != '%')
                return 1;
            else
                return 0;
        }
        return p;
    }
    
    /** Emits a valid Spin preamble 
     *
     *
        Minimal Spin bootstrap code for assembly language launch
        --------------------------------------------------------
        $0000: HZ HZ HZ HZ CR CS 10 00 LL LL 18 00 18 00 10 00
        $0010: FF FF F9 FF FF FF F9 FF 35 37 04 35 2C -- -- --
        $0020: your assembly code starts here - loaded into COG #0

        elaboration:
        $0000: HZ HZ HZ HZ - internal clock frequency in Hz (long)
        $0004: CR          - value to be written to clock register (byte)
        $0005: CS          - checksum so that all RAM bytes will sum to 0 (modulus 256)
        $0006: 10 00       - 'pbase' (word) must be $0010
        $0008: LL LL       - 'vbase' (word) number of longs loaded times 4
        $000A: 18 00       - 'dbase' (word) above where $FFF9FFFF's get placed
        $000C: 18 00       - 'pcurr' (word) points to Spin code
        $000E: 10 00       - 'dcurr' (word) points to local stack
        $0010: FF FF F9 FF - below local stack, must be $FFF9FFFF
        $0014: FF FF F9 FF - below local stack, must be $FFF9FFFF
        $0018: 35          - push #0   (long written to $0010)
        $0019: 37 04       - push #$20 (long written to $0014)
        $001B: 35          - push #0   (long written to $0018)
        $001C: 2C          - COGINIT(0, $20, 0) - load asm code from $20+ into same COG #0
        $001D: -- -- --    - filler
        $0020: XX XX XX XX - 1st long of asm program to be loaded into COG #0
        $0024: XX XX XX XX - 2nd long of asm program to be loaded into COG #0
        $0028:             - rest of data

        Note: 'vbase' is the total number of bytes loaded.  If valid, the IDE
        will load the binary file and transfer it to a Propellor.
     */
    
    protected void emitPreamble(java.io.FileOutputStream bin) {
        // Freq
        long t = getFullSymbolAddr("CLKFREQ");
        if (t == 0) t = 80000000;
        try {
            bin.write((int)(t >> 24) & 0xff);
            bin.write((int)(t >> 16) & 0xff);
            bin.write((int)(t >> 8) & 0xff);
            bin.write((int)(t) & 0xff);
            
            // CS
            if (symbolExists("CLKMODE")) 
                t = getFullSymbolAddr("CLKMODE");
            else 
                t = 0x4f; // Xtal1 + PLLx16
            bin.write((int)(t) & 0xff);
            // checksum...
            bin.write(0);
            // 
            
            
        } catch (java.io.IOException e) {
            
        }
        
            
        
    }
    
    
    
    protected class Instruction {
        protected byte[] data;
        protected int addr;
        protected int size;
        protected Symbol sym;
        
        public Instruction() {
            data = new byte[32];
            size = 0;
        }
        public void addLong(long o) {
            if (size + 4 > data.length) {
                byte[] d = new byte[size + 32];
                System.arraycopy(data, 0, d, 0, size);
                data = null;
                data = d;
            }
            data[size + 3] = (byte) ((o >> 24) & 0xff);
            data[size + 2] = (byte) ((o >> 16) & 0xff);
            data[size + 1] = (byte) ((o >> 8) & 0xff);
            data[size] = (byte) (o & 0xff);
            size += 4;
        }
        
        public void addWord(int o) {
            if (size + 2 > data.length) {
                byte[] d = new byte[size + 32];
                System.arraycopy(data, 0, d, 0, size);
                data = null;
                data = d;
            }
            data[size + 1] = (byte) ((o >> 8) & 0xff);
            data[size] = (byte) (o & 0xff);
            size += 2;
        }
        
        public void addByte(int o) {
            if (size + 1 > data.length) {
                byte[] d = new byte[size + 32];
                System.arraycopy(data, 0, d, 0, size);
                data = null;
                data = d;
            }
            data[size] = (byte) (o & 0xff);
            size++;
        }
        
        public void addString(String s) {
            int l = s.length() - 1;
            if (size + s.length() > data.length) {
                byte[] d = new byte[size + l + 3];
                System.arraycopy(data, 0, d, 0, size);
                data = null;
                data = d;
            }
            
            for (int j = 1; j < l; j++)
                data[size + j - 1] = (byte) s.charAt(j);
            size += l - 1;
        }
        
        public void setData(byte[] d) {
            data = d;
            size = d.length;
        }
        public void setAddr(int a) {
            addr = a;
        }
        
        public void setSize(int s) {
            size = s;
        }
        
        public void setSymbol(Symbol s) {
            sym = s;
        }
        
        public byte[] getData() {
            return data;
        }
        
        public int getAddr() {
            return addr;
        }
        
        public int getSize() {
            return size;
        }
        
        public Symbol getSymbol() {
            return sym;
        }
        
        public String getSymbolname() {
            return sym.getSymbolName();
        }
    }
    
    /** This class provides a nice way to handle a line of code
     * it parses some text and collects all pertinent info for easy
     * compiling
     *
     * No argument checking is performed, only valid instructions and conditions
     * are handled
     *
     */
    private class TokenizeCode {
        protected String symbol;
        protected String condition;
        protected String instruction;
        protected java.util.Vector <String> args;
        protected java.util.Vector <String> effects;
        protected String[] opcodes = { "wrbyte",  "rdbyte",  "wrword",  "rdword",  "wrlong",  "rdlong",   "clkset",  "cogid",  
                                   "coginit", "cogstop", "locknew", "lockret", "lockset", "lockclr",  "mul",     "muls",   
                                   "enc",     "ones",    "ror",     "rol",     "shr",     "shl",      "rcr",     "rcl",
                                   "sar",     "rev",     "mins",    "maxs",    "min",     "max",      "movs",    "movd",   
                                   "movi",    "jmp",     "call",    "ret",     "test",    "and",      "testn",   "andn",   
                                   "or",      "xor",     "muxc",    "muxnc",   "muxz",    "muxnz",    "add",     "cmp",    
                                   "sub",     "addabs",  "subabs",  "sumc",    "sumnc",   "sumz",     "sumnz",   "mov", 
                                   "neg",     "abs",     "absneg",  "negc",    "negnc",   "negz",     "negnz",   "cmps",
                                   "cmpsx",   "addx",    "cmpx",    "subx",    "adds",    "subs",     "addsx",   "subsx",
                                   "cmpsub",  "djnz",    "tjnz",    "tjz ",    "waitpeq", "waitpne ", "waitcnt", "waitvid", 
                                   "nop",     "=",       "long",    "word",    "byte",    "string",   "res",     "preamble" };
    
        protected String[] directives = { ".global", ".section", ".align", ".include" };
    
        protected String[] conds = { "if_always",   "if_nc_and_nz", "if_nc_and_z", "if_nc", 
                                 "if_c_and_nz", "if_nz",        "if_c_ne_z",   "if_nc_or_nz", 
                                 "if_c_and_z",  "if_c_eq_z",    "if_z",        "if_nc_or_z", 
                                 "if_c",        "if_c_or_nz",   "if_c_and_z",  "if_never",
                                 "if_e",        "if_ne",        "if_a",        "if_b",
                                 "if_ae",       "if_be",        "if_z_eq_c",   "if_z_ne_c",
                                 "if_z_and_c",  "if_z_and_nc",  "if_nz_and_c", "if_nz_and_nc",
                                 "if_z_or_c",   "if_z_or_nc",   "if_nz_or_c",  "if_nz_or_nc"
                                };
                                
        
        protected String[] effs = { "wc", "wz", "wr" };
        
        protected int err;
        
        protected final int ERR_NONE      = 0; // no error
        protected final int ERR_SYMEXISTS = -1; // symbol exists
        protected final int ERR_UKNCOND   = -2; // unknown condition
        protected final int ERR_UKNINST   = -3; // unknown instruction
        protected final int ERR_SYMNF     = -4; // symbol not found
        protected final int ERR_EMPTY     = -5; // empty line
        protected final int ERR_DUPWX     = -6; // duplicated WC or WZ field
        protected final int ERR_ARGMISS = -7; // Argument(s) missing
        protected final int ERR_RDONLY = -8; // memory position is read-only (INA/INB)
        protected final int ERR_GARBAGE = -9; // garbage at end of line
        protected final int ERR_SECTTOOLONG = -10; // section too long
        protected final int ERR_DUPSECT = -11; //Duplicated section
        protected final int ERR_DIRNOARGS = -12; // Too few arguments for directive
    
        public TokenizeCode() {
            
        }
        
        public int tokenize(String line) {
            err = 0;
            symbol = null;
            condition = null;
            instruction = null;
            args = new java.util.Vector<String>();
            effects = new java.util.Vector<String>();
            separate(line);
            if (instruction == null) {
                if (effects.size() > 0 || args.size() > 0 || condition != null)
                    err = ERR_UKNINST;
            }
            return err;
        }
        
        public String getSymbol() {
            return symbol;
        }
        
        public String getCondition() {
            return condition;
        }
        
        public String getInstruction() {
            return instruction;
        }
        
        public java.util.Vector<String> getArgs() {
            return args;
        }
        
        public java.util.Vector<String> getEffects() {
            return effects;
        }
        
        /**
         * Separates a line by whitespace (and commas)
         *
         * symbol cond instr arg1,arg2 eff1 eff2 eff3
         *
         * @param l String line to separate
         *
         *
         * 
         */
        protected void separate(String l) {
            String part;
            int ip, fp, idx = 0;
            boolean str = false, str0 = false;
            ip = 0;
            fp = 0;
            String nl = l.trim();
            err = ERR_NONE;
            // discards rest of line after \' ; or {
            while ((fp < nl.length()) && (nl.charAt(fp) != ';') && (nl.charAt(fp) != '{')) {
                if (nl.charAt(fp) == '\'')
                    if (!str)
                        break;
                switch (nl.charAt(fp)) {
                    case 9:
                    case 32:
                    case ',':
                        if (str) {
                            fp++;
                            break;
                        }
                        if (ip >= fp) { 
                            fp++;
                            ip = fp;
                            break;
                        }
                        part = nl.substring(ip, fp);
                        if (idx == 0) {
                            if (this.isCondition(part)) {
                                condition = part;
                            } else
                            if (this.isInstruction(part)) {
                                instruction = part;
                            } else
                            if (this.isDirective(part)) {
                                instruction = part;
                            }
                            else symbol = part;
                        } else {
                            if (this.isCondition(part)) {
                                if (condition != null) {
                                    err = ERR_UKNINST;
                                    return;
                                }
                                condition = part;
                            } else
                            if (this.isInstruction(part)) {
                                if (instruction != null) {
                                  err = ERR_ARGMISS;
                                  return;
                                }
                                    instruction = part;
                            } else
                            if (this.isDirective(part)) {
                                if (instruction != null) {
                                    err = ERR_ARGMISS;
                                    return;
                                }
                                instruction = part;
                            } else {
                                if (this.isEffect(part)) {
                                    if (instruction != null)
                                        effects.add(part);
                                    else {
                                        err = ERR_ARGMISS;
                                        return;
                                    }
                                }
                                else {
                                    if (instruction == null)
                                        symbol = part;
                                    else
                                        args.add(part);
                                }
                            }
                        }
                        idx++;
                        fp++;
                        ip = fp;
                        break;
                    case '=': // is also a separator, but is included
                        if (str) {
                            fp++;
                            break;
                        }
                        if (ip >= fp) {
                            fp++;
                            ip = fp;
                            break;
                        }
                        part = nl.substring(ip, fp);
                        if (instruction != null || condition != null || effects.size() > 0) {
                            err = ERR_ARGMISS;
                            return;
                        }
                        instruction = "=";
                        args.add(part);
                        fp++;
                        ip = fp;
                        break;
                    case '\"':
                        if (str) {
                            if (idx > 0) {
                                args.add(nl.substring(ip, fp + 1));
                                fp += 2;
                                ip = fp;
                                str = false;
                                break;
                            }
                            err = ERR_GARBAGE;
                            return;
                        }
                        str = true;
                        ip = fp++;
                        break;
                   
                    default:
                        fp++;
                } // switch
            } // while
        
            if (ip < fp) {
                part = nl.substring(ip, fp);
                if (this.isCondition(part)) {
                    if (condition != null) {
                        err = ERR_UKNINST;
                        return;
                    }
                    condition = part;
                } else
                if (this.isInstruction(part)) {
                    if (instruction != null) {
                        err = ERR_ARGMISS;
                        return;
                    }
                    instruction = part;
                } else
                if (this.isDirective(part)) {
                    if (instruction != null) {
                        err = ERR_ARGMISS;
                        return;
                    }
                    instruction = part;
               } else {
               if (this.isEffect(part)) {
                    if (instruction != null)
                        effects.add(part);
                    else {
                        err = ERR_ARGMISS;
                        return;
                    }
               } else {
                   if (instruction == null)
                       symbol = part;
                   else
                       args.add(part);
               }
               }
            }
            return;
        }
        
        protected boolean isCondition(String s) {
            int l = conds.length;
            if (s == null)
                return false;
        
            for (int i = 0; i < l; i++)
                if (s.equalsIgnoreCase(conds[i]))
                    return true;
            return false;
        }
    
        protected boolean isInstruction(String s) {
            int l = opcodes.length;
            if (s == null)
                return false;
        
            for (int i = 0; i < l; i++)
                if (s.equalsIgnoreCase(opcodes[i]))
                    return true;
            return false;
        }
        
        protected boolean isEffect(String s) {
            int l = effs.length;
            if (s == null)
                return false;
        
            for (int i = 0; i < l; i++)
                if (s.equalsIgnoreCase(effs[i]))
                    return true;
            return false;
        }
        protected boolean isDirective(String s) {
            int l = directives.length;
            if (s == null)
                return false;
        
            for (int i = 0; i < l; i++)
                if (s.equalsIgnoreCase(directives[i]))
                    return true;
            return false;
        }
    }
    
    /** This class provides a nice way to handle a line of code, filled in the 1st pass
     * and used in the second pass
     * this way we do not have to parse a line twice
     */
    protected class Tokenized {
        public int linenum;
        public String symbol;
        public String condition;
        public String instruction;
        public java.util.Vector <String> args;
        public java.util.Vector <String> effects;
    }
    
}
